#pragma once
#include<iostream>
#include<unordered_map>
#include"Epoller.hpp"
#include"Connection.hpp"

//事件派发
class Reactor
{
        const static int gnum = 64;
        public:
        Reactor()
        :_isrunning(false)
        {}
        void AddConnection(int fd,uint32_t events,func_t recver,func_t sender,func_t excepter)//添加连接
        {
                //1:构建Connection
                Connection *conn = new Connection(fd);
                conn->SetEvents(events);
                conn->Register(recver,sender,excepter);
                conn->SetSelf(this);
                //2:向内核中设定对fd的关心
                _epller.AddEven(conn->Sockfd(),conn->Events());
                //3:向_connections添加connectioin对象
                _connections.insert(std::make_pair(conn->Sockfd(),conn));
        }
        void EnableReadWrite(int sockfd,bool readable,bool writeable)
        {
            uint32_t events = (readahead?EPOLLIN:0)|(writeable?EPOLLOUT:0)|EPOLLET;
            if(ConnectionIsExists(sockfd))//判断连接是否存在参数文件描述符
            {   
                // 修改写的connection关心的事件
                _connections[sockfd]->SetEvents(events);
                //写透到内核中
                _epller.ModEvent(sockfd,events);
            }
        }
        void RemoveConnection(int sockfd)//删除连接
        {
            if(!ConnectionIsExists(sockfd))
            {
                return;
            }
            //去掉epoll对该文件fd的关心
            _epller.DelEvent(sockfd);
            //服务器关掉sockfd
            _connections[sockfd]->Close();
            //释放connection
            delete _connections[sockfd];
            _connections.erase(sockfd);
        }
        bool ConnectionIsExists(int sockfd)
        {
            auto iter = _connections.find(sockfd);
            return iter!=_connections.end();
        }
        void LoopOnce(int timeout)
        {
            int n = _epller.Wait(revs,gnum,timeout);
            for(int i = 0;i<n;i++)
            {
                int sockfd = revs[i].data.fd;
                uint32_t revents = revs[i].events;
                if(revents & EPOLLIN)
                {
                    revents |= (EPOLLIN | EPOLLOUT);
                }
                if(revents & EPOLLERR)
                {
                    revents |=(EPOLLIN|EPOLLOUT);
                }
                if(revents & EPOLLIN)
                {
                    if(ConnectionIsExists(sockfd)&&_connections[sockfd]->_recver!=nullptr)
                    {
                        _connections[sockfd]->_recver(_connections[sockfd]);
                    }
                }
                if(revents & EPOLLOUT)
                {
                    if(ConnectionIsExists(sockfd)&&_connections[sockfd]->_sender!=nullptr)
                    {
                        _connections[sockfd]->_sender(_connections[sockfd]);
                    }
                }

            }
        }
        //事件派发核心函数
        void Dispatcher()//派送
        {
            _isrunning = true;
            int  timeout = 3000;
            while (_isrunning)
            {
                LoopOnce(timeout);
                //处理其他时间
            }
            _isrunning = false;
            
        }
        void Debug()
        {
            std::cout<<"==============================="<<std::endl;
            for(auto &Connection : _connections)
            {
                std::cout<<"fd :" <<Connection.second->Sockfd()<<",";
                uint32_t events = Connection.second->Events();
                if((events & EPOLLIN)&&events & EPOLLET)
                {
                    std::cout<<"EPOLLIN|EPOLLET";
                }
                if((events & EPOLLOUT)&&(events *EPOLLET))
                {
                    std::cout<<"EPOLLOUT|EPOLLET";
                    std::cout <<std::endl;
                }
            }
            std::cout<<"----------------------------"<<std::endl;
        }
        ~Reactor()
        {
            
        }
    private:
    std::unordered_map<int ,Connection*> _connections;
    struct epoll_event revs[gnum];
    Epoller _epller;
    bool _isrunning;
};